package org.apache.ocean.main.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.ocean.main.replication.ByteBufferPool;
import org.apache.ocean.main.replication.ByteBufferPool.ByteBuffer;
import org.apache.ocean.main.replication.ByteBufferPool.ByteBuffers;

public class ByteArrayInputStream extends InputStream {
	private List<ByteBuffer> byteBuffers;
  private long length;

  private ByteBuffer currentBuffer;
  private int currentBufferIndex;
  
  private int bufferPosition;
  private long bufferStart;
  private int bufferLength;
  private int bufferSize;
  
  public ByteArrayInputStream(ByteBuffers byteBuffers) throws IOException {
  	this.length = byteBuffers.getLength();
  	int numRead = 0;
  	this.byteBuffers = byteBuffers.getByteBuffers();
    currentBufferIndex = -1;
    currentBuffer = null;
  }
  
  public ByteArrayInputStream(int bufferSize, InputStream input, int length, ByteBufferPool byteBufferPool) throws IOException {
  	this.length = length;
  	int numRead = 0;
  	byteBuffers = new ArrayList<ByteBuffer>();
		while (true) {
			ByteBuffer byteBuffer = byteBufferPool.get(bufferSize);
			int n = input.read(byteBuffer.getBytes());
			if (n == -1) {
				byteBuffer.finished();
				break;
			}
			numRead += n;
			byteBuffers.add(byteBuffer);
		}
		if (length != numRead) {
			throw new IOException("num read different than length");
		}
    currentBufferIndex = -1;
    currentBuffer = null;
  }

  public void close() {
    for (ByteBuffer byteBuffer : byteBuffers) {
    	byteBuffer.finished();
    }
  }

  public long length() {
    return length;
  }

  public int read() throws IOException {
    if (bufferPosition >= bufferLength) {
      currentBufferIndex++;
      switchCurrentBuffer();
    }
    return currentBuffer.getBytes()[bufferPosition++];
  }

  public void readBytes(byte[] b, int offset, int len) throws IOException {
    while (len > 0) {
      if (bufferPosition >= bufferLength) {
        currentBufferIndex++;
        switchCurrentBuffer();
      }
      int remainInBuffer = bufferLength - bufferPosition;
      int bytesToCopy = len < remainInBuffer ? len : remainInBuffer;
      System.arraycopy(currentBuffer, bufferPosition, b, offset, bytesToCopy);
      offset += bytesToCopy;
      len -= bytesToCopy;
      bufferPosition += bytesToCopy;
    }
  }

  private final void switchCurrentBuffer() throws IOException {
    if (currentBufferIndex >= byteBuffers.size()) {
      // end of file reached, no more buffers left
      throw new IOException("Read past EOF");
    } else {
      currentBuffer = byteBuffers.get(currentBufferIndex);
      bufferPosition = 0;
      bufferStart = (long) bufferSize * (long) currentBufferIndex;
      long buflen = length - bufferStart;
      bufferLength = buflen > bufferSize ? bufferSize : (int) buflen;
    }
  }
}
